package cn.jja8.config.tool;

import cn.jja8.config.tool.work.FieldAsManager;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.LinkedHashMap;
import java.util.Map;

public class Config<T extends Config<T>>{
    protected final FieldAsManager fieldAsManager;
    protected Map<String,Object> configMap;
    protected Class<? extends Annotation> annotationClass;
    protected String prefix;
    protected boolean writeDefault = true;

    public Config() {
        fieldAsManager = new FieldAsManager();
        configMap = new LinkedHashMap<>();
        annotationClass = Conf.class;
    }

    private Config(FieldAsManager fieldAsManager, Map<String, Object> configMap, Class<? extends Annotation> annotationClass, String prefix, boolean writeDefault) {
        this.fieldAsManager = fieldAsManager;
        this.configMap = configMap;
        this.annotationClass = annotationClass;
        this.prefix = prefix;
        this.writeDefault = writeDefault;
    }

    public FieldAsManager getMapFieldAs() {
        return fieldAsManager;
    }

    /**
     * 设置yaml
     * */
    public T setConfigMap(Map<String,Object> configMap) {
        this.configMap = configMap;
        return (T) this;
    }

    /**
     * 设置加载的枚举
     * 指定加载带有此枚举的static字段
     */
    public T setAnnotationClass(Class<? extends Annotation> annotationClass) {
        this.annotationClass = annotationClass;
        return (T) this;
    }

    /**
     * 设置配置文件前缀
     * */
    public T setPrefix(String prefix) {
        this.prefix = prefix;
        return (T) this;
    }

    /**
     * 设置如果配置文件中不存在static上的字段值，是否将static字段上的当前值写入到配置文件中。
     * */
    public T setWriteDefault(boolean writeDefault) {
        this.writeDefault = writeDefault;
        return (T) this;
    }

    /**
     * 将配置文件中的值加载到具有 config 注解的static字段上
     * @param toClass 要加载static变量的类的class
     * */
    public T as(Class<?> toClass){
        return as(toClass,null);
    }
    /**
     * 将配置文件中的值加载到具有 config 注解的字段上,字段非static
     * @param obj 要加载static变量的类的class
     * */
    public T as(Object obj){
        return as(obj.getClass(),obj);
    }
    /**
     * 将配置文件中的值加载到具有 config 注解的static字段上
     * @param toClass 要加载static变量的类的class
     * */
    private T as(Class<?> toClass,Object obj){
        if (annotationClass==null){
            throw new Error("未设置annotationClass");
        }
        for (Field declaredField : toClass.getDeclaredFields()) {
            if (obj==null){
                if (!Modifier.isStatic(declaredField.getModifiers())) {
                    continue;
                }
            }else {
                if (Modifier.isStatic(declaredField.getModifiers())) {
                    continue;
                }
            }
            Annotation annotation = declaredField.getDeclaredAnnotation(annotationClass);
            if (annotation==null) {
                continue;
            }
            String name = prefix==null?declaredField.getName():prefix+declaredField.getName();
            Annotation inside = declaredField.getType().getAnnotation(annotationClass);
            Object v = get(name);
            if (inside!=null){
                try {
                    declaredField.setAccessible(true);
                    Object insideObj = declaredField.get(obj);
                    if (insideObj==null){
                        throw new Error(declaredField.getClass().getName()+"."+declaredField.getName()+"的值为null，无法加载！");
                    }
                    //嵌套配置
                    if (v!=null){
                        new Config<>(fieldAsManager,(Map<String, Object>) v,annotationClass,null,writeDefault).as(insideObj);
                    }else if (writeDefault){
                        set(name,new Config<>(fieldAsManager,new LinkedHashMap<>(),annotationClass,null,writeDefault).as(insideObj).configMap);
                    }
                }catch (Throwable throwable){
                    System.err.println("{'"+name+"':'"+v+"'}读取时出错！原因："+throwable.getMessage());
                    throwable.printStackTrace();
                }
                continue;
            }

            if (v!=null){
                try {
                    fieldAsManager.read(declaredField,obj , v);
                }catch (Throwable throwable){
                    System.err.println("{'"+name+"':'"+v+"'}读取时出错！原因："+throwable.getMessage());
                    throwable.printStackTrace();
                }
            }else if (writeDefault){
                try {
                    set(name,fieldAsManager.write(declaredField,obj ));
                }catch (Throwable throwable){
                    System.err.println("{'"+name+"':'****'}写入时出错！原因："+throwable.getMessage());
                    throwable.printStackTrace();
                }
            }
        }
        return (T) this;
    }


    /**
     * 设置某值
     * */
    private void set(String key,Object v){
        String[] keys = key.split("\\.");
        Map in = configMap;
        for (int i = 0; i < keys.length-1; i++) {
            Object map = in.get(keys[i]);
            if (!(map instanceof Map)){
                in.put(keys[i],in=new LinkedHashMap());
            }else{
                in = (Map) map;
            }
        }
        in.put(keys[keys.length-1],v);
    }

    /**
     * 获取某值
     * */
    private Object get(String key){
        String[] keys = key.split("\\.");
        Object in = configMap;
        for (String s : keys) {
            if (in instanceof Map){
                if ((in = ((Map)in).get(s))==null) {
                    return null;
                }
            }else {
                return null;
            }
        }
        return in;
    }
}
